#region (c) 2014 THN Solutions LLC. - All rights reserved
/*
THN DATA ABSTRACTION LAYER version 1

Copyright (c) 2014, THN Solutions LLC. ( www.ThnSolutions.com )
Author: Nguyễn, M. Hải                 ( www.Minh-Hai.com     )
All rights reserved.

This library is dual-licensed.
  + For commercial software, please obtain a commercial license from THN Solutions LLC.

  + For free software, this library is licensed under GPL version 3. A summary of GPLv3 is
    listed below. You should also find a copy of GPLv3 as file License_GPLv3.txt included
	with the source-code files.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 3 as published by
    the Free Software Foundation  of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#endregion

#region Record of Change
//             							CHANGE HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | DEVELOPER  | DESCRIPTION                                              |
// -------------------------------------------------------------------------------------
// | 24-Apr-06 | HaiNM      | First creation.                                          |
// -------------------------------------------------------------------------------------
//
#endregion

#region Record of Audit
//             							REVIEW HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | REVIEWER   | NOTES                                                    |
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
//
#endregion

#region Using directives
using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;

using Thn;
using Thn.Data;

#endregion

namespace Demo
{
    /// <summary>
    /// 
    /// </summary>
#if DEBUG
    public
#else
    public
#endif
 partial class fmMain : Form
    {
        #region Fields
        IDatabase db;
        Random ran = new Random();
        #endregion

        #region Properties
        #endregion

        #region UI Events processing

        #region btnConnect_Click
        private void btnConnect_Click(object sender, EventArgs e)
        {
            Connect();
        }
        #endregion

        #region btnGenerate_Click
        private void btnGenerate_Click(object sender, EventArgs e)
        {
            #region sanity check
            if (db == null)
            {
                MessageBox.Show("Please connect to database first");
                return;
            }
            #endregion

            /*
             * When the table structure are not yet available in the database,
             * we can generate it automatically by code.
             */

            //register the types that will be generated
            db.Register(typeof(User));

            //generate
            db.CreateStructure(false);

            Log.Debug("Tables structure generated");

            //generate test data for demonstration
            GenerateSampleData();
        }
        #endregion

        #region btnInsert_Click
        private void btnInsert_Click(object sender, EventArgs e)
        {
            #region sanity check
            if (db == null)
            {
                MessageBox.Show("Please connect to database first");
                return;
            }
            #endregion

            //create the business object to be inserted
            var u = new User();
            u.Name = "Test user created at" + DateTime.Now.ToLongTimeString();
            u.ID = Guid.NewGuid();

            //save it
            //There are two sets of API for working with objects.
            //The xxxObjects are immediate mode. When calling these methods, the operations are carried out right away.
            //The xxx (without Objects suffix) are deferred mode. These methods allowed us to mix and match commands
            //to our heart's content until an Execute() is called.

            //-------------------- Example of immediate mode
            db.InsertObjects(u);

            //quit this example
            //we quit this method here so that the example of deferred codes are not run. They are there for demonstration.
            Log.Debug("New user inserted");
            return;

            //-------------------- Example of deferred mode
            var q = db.Insert(u);

            //let's say we want to insert another object
            var user2 = new User();
            db.Insert(user2);

            //both of the above will not save to the database, until we call the following
            q.Execute();

            //We can also use the fluent API to save us from typing too much, as following
            db.Insert(u)
              .Insert(user2)
              .Execute();

            //The deferred mode is the recommended approach, we'll see its power in transaction example below

        }
        #endregion

        #region btnSelectAll_Click
        private void btnSelectAll_Click(object sender, EventArgs e)
        {
            #region sanity check
            if (db == null)
            {
                MessageBox.Show("Please connect to database first");
                return;
            }
            #endregion

            //Select all users and return as a list, sorted by property Name
            var lst = db.Select
                        .From(typeof(User))
                        .Execute()
                        .AsSortedList("Name");

            //if we don't want a sorted list, use Execute().AsList instead

            Log.Debug("Select All - {0} items", lst.Count);
            foreach (var item in lst)
            {
                Log.Debug("   {0}", item);
            }
        }
        #endregion

        #region btnSelectWithCondition_Click
        private void btnSelectWithCondition_Click(object sender, EventArgs e)
        {
            #region sanity check
            if (db == null)
            {
                MessageBox.Show("Please connect to database first");
                return;
            }
            #endregion

            //The fluent API is great for select query
            //In this example, we'll select user with name = User 1
            //Since we expect only one result, we'll execute as single object
            var result = db.Select
                           .From(typeof(User))
                           .Where
                                .Equal("Name", "User 1")
                           .Execute()
                           .AsSingleObject;

            if (result == null) Log.Debug("Could not find such user");
            else Log.Debug("{0}", result);
        }
        #endregion

        #region btnDelete_Click
        private void btnDelete_Click(object sender, EventArgs e)
        {
            #region sanity check
            if (db == null)
            {
                MessageBox.Show("Please connect to database first");
                return;
            }
            #endregion

            //select user with Name = "User 1"
            var obj = db.Select
                        .From(typeof(User))
                        .Where
                            .Equal("Name", "User 1")
                        .Execute()
                        .AsSingleObject;

            //delete him
            if (obj != null)
            {
                db.Delete(obj).Execute();
                Log.Debug("User 1 deleted");
            }
            else Log.Debug("User 1 does not exist");
        }
        #endregion

        #region btnTransaction_Click
        private void btnTransaction_Click(object sender, EventArgs e)
        {
            #region sanity check
            if (db == null)
            {
                MessageBox.Show("Please connect to database first");
                return;
            }
            #endregion

            /*
             * Transaction is a very powerful feature of this library.
             * Combined with deferred mode operation, transaction can be nested in unlimited scopes.
             * The transaction as well as nesting is supported automatically regardless of whether
             * the underlying database has this feature or not.
             */

            //Let's say we want to insert two users in the same transaction, and both must be successful.
            //Each transaction is encapsulated within a Begin and End.
            var u1 = new User() { Name = "Transaction user 1", ID = Guid.NewGuid() };
            var u2 = new User() { Name = "Transaction user 2", ID = Guid.NewGuid() };
            db.Begin
                    .Insert(u1)
                    .Insert(u2)
              .End
              .Execute();

            //if the transaction failed, an exeception will be thrown and we will not reach this line
            Log.Debug("Transaction executed successfully");
            return;

            //If we want to have nested transaction, we simply add more begin/end.
            //The following is nonsense, but just to demonstrate our point.
            db.Begin
                    .Begin
                        .Begin
                            .Insert(u1)
                            .Insert(u2)
                        .End
                    .End
                .End
                .Execute();
        }
        #endregion

        #endregion

        #region Connect
        void Connect()
        {
            #region Step 1: Choose a database
            /*
             * We need to choose a database class that supports the actual database to be used.
             * The current version support these database:
             *   + Microsoft Access
             *   + Microsoft SQL Server
             *   + MySQL
             *   + In-memory: best for quick testing or for caching
             *   + Xml: best for standalone applications that do not need dependency on Microsoft or MySQL
             *          XML DB is also great for carrying a seed database from development to export
             *          to production database.
             */

            //Microsoft access
            //db = new Thn.Data.Database.Sql.AccessDatabase();

            //Microsoft SQL server
            //db = new Thn.Data.Database.Sql.MsSqlDatabase();

            //MySQL
            //db = new Thn.Data.Database.Sql.MySql.MySqlDatabase();

            //In-memory
            //db = new Thn.Data.Database.Memory.InMemoryDatabase();

            //XML
            db = new Thn.Data.Database.Memory.XmlDatabase();

            #endregion

            #region Step 2: Connection string

            string connectionString;

            //You need to consult with the database vendor to get the exact connection string they require.

            //For Microsoft SQL Server, the connection might look like the following
            //connectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=Test;Integrated Security=True";

            //For this example, we'll use an XML database, the connection string is simply the path to the database file.
            connectionString = "Test";

            #endregion

            #region Step 3: Initialize

            //connect to the database
            db.Initialize(connectionString);

            #endregion

            Log.Debug("Connected to database");

            //Note: some DB requires us to close its connection after we're done using it.
            //See Form Closing below for more information

            #region Special notes for Microsoft Access
            /*
             * For older versions of Microsoft Access, you will need to set the Platform Target for Thn.Data.dll
             * and your application to x86. Otherwise, you will encounter errors while connecting to MS Access driver.
             */
            #endregion
        }
        #endregion

        #region Generate Sample Data
        /// <summary>
        /// Generate test data for demonstration
        /// </summary>
        void GenerateSampleData()
        {
            for (int i = 1; i <= 10; i++)
            {
                var u = new User();
                u.Name = "User " + i.ToString();
                u.ID = Guid.NewGuid();
                u.DOB = new DateTime(2000, 1, 1);
                db.InsertObjects(u);
            }
        }
        #endregion

        #region Form Closing
        private void fmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            //make sure that we close the connection to the database
            //this step is necessary for certain databases to flush their cache, dispose connection resources, etc.
            if (db != null) db.Close();
        }
        #endregion

        #region On Load
        /// <summary>
        /// Initialize this form when form is loaded.
        /// </summary>
        protected override void OnLoad(EventArgs e)
        {
            if (!DesignMode)
            {
                //setup logging
                Log.Bind(mLogListener);
            }
            #region Defensive tracing
            else
            {
                //don't do anything at design-time
            }
            #endregion

            //let my ancestor process its own logic
            base.OnLoad(e);
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor.
        /// </summary>
        public fmMain()
        {
            InitializeComponent();
        }
        #endregion
    }
}
